<html><head><meta charset="utf-8"/><title>错误处理</title></head><body><h1>错误处理</h1><div class="x-wiki-content x-main-content">
 <p>
  在执行JavaScript代码的时候，有些情况下会发生错误。
 </p>
 <p>
  错误分两种，一种是程序写的逻辑不对，导致代码执行异常。例如：
 </p>
 <pre><code>var s = null;
var len = s.length; // TypeError：null变量没有length属性
</code></pre>
 <p>
  对于这种错误，要修复程序。
 </p>
 <p>
  一种是执行过程中，程序可能遇到无法预测的异常情况而报错，例如，网络连接中断，读取不存在的文件，没有操作权限等。
 </p>
 <p>
  对于这种错误，我们需要处理它，并可能需要给用户反馈。
 </p>
 <p>
  错误处理是程序设计时必须要考虑的问题。对于C这样贴近系统底层的语言，错误是通过错误码返回的：
 </p>
 <pre><code>int fd = open("/path/to/file", O_RDONLY);
if (fd == -1) {
    printf("Error when open file!");
} else {
    // TODO
}
</code></pre>
 <p>
  通过错误码返回错误，就需要约定什么是正确的返回值，什么是错误的返回值。上面的
  <code>
   open()
  </code>
  函数约定返回
  <code>
   -1
  </code>
  表示错误。
 </p>
 <p>
  显然，这种用错误码表示错误在编写程序时十分不便。
 </p>
 <p>
  因此，高级语言通常都提供了更抽象的错误处理逻辑try ... catch ... finally，JavaScript也不例外。
 </p>
 <h3>
  try ... catch ... finally
 </h3>
 <p>
  使用try ... catch ... finally处理错误时，我们编写的代码如下：
 </p>
 <pre><code class="language-x-javascript">'use strict';
----
var r1, r2, s = null;
try {
    r1 = s.length; // 此处应产生错误
    r2 = 100; // 该语句不会执行
} catch (e) {
    console.log('出错了：' + e);
} finally {
    console.log('finally');
}
console.log('r1 = ' + r1); // r1应为undefined
console.log('r2 = ' + r2); // r2应为undefined
----
// 直接运行
</code></pre>
 <p>
  运行后可以发现，输出提示类似“出错了：TypeError: Cannot read property 'length' of null”。
 </p>
 <p>
  我们来分析一下使用try ... catch ... finally的执行流程。
 </p>
 <p>
  当代码块被
  <code>
   try { ... }
  </code>
  包裹的时候，就表示这部分代码执行过程中可能会发生错误，一旦发生错误，就不再继续执行后续代码，转而跳到
  <code>
   catch
  </code>
  块。
  <code>
   catch (e) { ... }
  </code>
  包裹的代码就是错误处理代码，变量
  <code>
   e
  </code>
  表示捕获到的错误。最后，无论有没有错误，
  <code>
   finally
  </code>
  一定会被执行。
 </p>
 <p>
  所以，有错误发生时，执行流程像这样：
 </p>
 <ol>
  <li>
   先执行
   <code>
    try { ... }
   </code>
   的代码；
  </li>
  <li>
   执行到出错的语句时，后续语句不再继续执行，转而执行
   <code>
    catch (e) { ... }
   </code>
   代码；
  </li>
  <li>
   最后执行
   <code>
    finally { ... }
   </code>
   代码。
  </li>
 </ol>
 <p>
  而没有错误发生时，执行流程像这样：
 </p>
 <ol>
  <li>
   先执行
   <code>
    try { ... }
   </code>
   的代码；
  </li>
  <li>
   因为没有出错，
   <code>
    catch (e) { ... }
   </code>
   代码不会被执行；
  </li>
  <li>
   最后执行
   <code>
    finally { ... }
   </code>
   代码。
  </li>
 </ol>
 <p>
  最后请注意，
  <code>
   catch
  </code>
  和
  <code>
   finally
  </code>
  可以不必都出现。也就是说，
  <code>
   try
  </code>
  语句一共有三种形式：
 </p>
 <p>
  完整的try ... catch ... finally：
 </p>
 <pre><code>try {
    ...
} catch (e) {
    ...
} finally {
    ...
}
</code></pre>
 <p>
  只有try ... catch，没有finally：
 </p>
 <pre><code>try {
    ...
} catch (e) {
    ...
}
</code></pre>
 <p>
  只有try ... finally，没有catch：
 </p>
 <pre><code>try {
    ...
} finally {
    ...
}
</code></pre>
 <h3>
  错误类型
 </h3>
 <p>
  JavaScript有一个标准的
  <code>
   Error
  </code>
  对象表示错误，还有从
  <code>
   Error
  </code>
  派生的
  <code>
   TypeError
  </code>
  、
  <code>
   ReferenceError
  </code>
  等错误对象。我们在处理错误时，可以通过
  <code>
   catch(e)
  </code>
  捕获的变量
  <code>
   e
  </code>
  访问错误对象：
 </p>
 <pre><code>try {
    ...
} catch (e) {
    if (e instanceof TypeError) {
        alert('Type error!');
    } else if (e instanceof Error) {
        alert(e.message);
    } else {
        alert('Error: ' + e);
    }
}
</code></pre>
 <p>
  使用变量
  <code>
   e
  </code>
  是一个习惯用法，也可以以其他变量名命名，如
  <code>
   catch(ex)
  </code>
  。
 </p>
 <h3>
  抛出错误
 </h3>
 <p>
  程序也可以主动抛出一个错误，让执行流程直接跳转到
  <code>
   catch
  </code>
  块。抛出错误使用
  <code>
   throw
  </code>
  语句。
 </p>
 <p>
  例如，下面的代码让用户输入一个数字，程序接收到的实际上是一个字符串，然后用
  <code>
   parseInt()
  </code>
  转换为整数。当用户输入不合法的时候，我们就抛出错误：
 </p>
 <pre><code class="language-x-javascript">'use strict';
----
var r, n, s;
try {
    s = prompt('请输入一个数字');
    n = parseInt(s);
    if (isNaN(n)) {
        throw new Error('输入错误');
    }
    // 计算平方:
    r = n * n;
    console.log(n + ' * ' + n + ' = ' + r);
} catch (e) {
    console.log('出错了：' + e);
}
----
// 直接运行
</code></pre>
 <p>
  实际上，JavaScript允许抛出任意对象，包括数字、字符串。但是，最好还是抛出一个Error对象。
 </p>
 <p>
  最后，当我们用catch捕获错误时，一定要编写错误处理语句：
 </p>
 <pre><code>var n = 0, s;
try {
    n = s.length;
} catch (e) {
    console.log(e);
}
console.log(n);
</code></pre>
 <p>
  哪怕仅仅把错误打印出来，也不要什么也不干：
 </p>
 <pre><code>var n = 0, s;
try {
    n = s.length;
} catch (e) {
}
console.log(n);
</code></pre>
 <p>
  因为catch到错误却什么都不执行，就不知道程序执行过程中到底有没有发生错误。
 </p>
 <p>
  处理错误时，请不要简单粗暴地用
  <code>
   alert()
  </code>
  把错误显示给用户。教程的代码使用
  <code>
   alert()
  </code>
  是为了便于演示。
 </p>
</div>
</body></html>